package com.pat.jrgwt.client;

import java.util.Date;
import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.pat.jrgwt.shared.FuturesContractsAmountView;
import com.pat.jrgwt.shared.FuturesTradesAmountView;
import com.pat.jrgwt.shared.MyConstants;

/**
 * 
 * Class for storage and getting information from ITTC server
 * 
 * @author Aleksej V.
 *
 */
public class ValuesStorage
{

    /**
     * Contains current year for calculations
     */
    @SuppressWarnings("deprecation")
    protected int m_currentYear = new Date().getYear() + 1900;
    
    /**
     * Contains flag to mark which data is loaded
     */
    protected LoadedDataView m_loadedFlag = LoadedDataView.getInstance();
    
    /**
     * Holds profit/losses for every currency for current year
     */
    protected final ProfitLossesHolder m_profitLossesHolder = new ProfitLossesHolder();
//    protected HashMap<String, double[]> m_arProfitLosses = new HashMap<String, double[]>();
    
    private static class SingletonHolder
    {
        private static final ValuesStorage instance = new ValuesStorage();
    }
    
    public static ValuesStorage getInstance()
    {
        return SingletonHolder.instance;
    }
    
    public boolean isProfitLossesFuturesLoaded()
    {
        return m_loadedFlag.m_isProfitLossesFuturesLoaded;
    }
    
    public void loadData()
    {
        JrcEventsLog.getInstance().add("Loading data from ITTC...");
        
        m_loadedFlag.resetAll();
        
        loadEuroConversion(m_currentYear);
        loadArchivePerformance(m_currentYear);
        loadProfitLossesFx(m_currentYear);
        loadProfitLossesFutures(m_currentYear);
        loadDvRatio(m_currentYear);
        loadFuturesTradesAmount(m_currentYear);
        loadFuturesContractsAmount(m_currentYear);
    }

    /**
     * Handles successful load of profit/losses (Forex) data from ITTC server
     * @param year
     * @param data
     */
    protected void onProfitLossesFxLoaded(int year, HashMap<String, double[]> data)
    {
        m_loadedFlag.setIsProfitLossesFxLoaded(true);
        m_profitLossesHolder.setDataFx(year, data);
        GWT.log("Profit/losses (Forex) are loaded");
        FxPipsView.getInstance().recalculate();
        FxWeighting.getInstance().recalculate();
        FxPositionSize.getInstance().recalculate();
        ProfitLossesView.getInstance().recalculate();
        
        onCommonLoad();
    }

    protected void onProfitLossesFuturesLoaded(int year, HashMap<String, double[]> data)
    {
        GWT.log("Profit/losses (Futures) are loaded");
        LoadedDataView.getInstance().setIsProfitLossesFuturesLoaded(true);
        m_profitLossesHolder.setDataFutures(year, data);
        
        ProfitLossesPipsPor3 pipsView = ProfitLossesPipsPor3.getInstance();
        // Refresh headers
        pipsView.setHeaders();
        pipsView.redrawFull();
        
        onCommonLoad();
    }

    /**
     * Handles successful load of archive performance data from ITTC server. Archive performance is recalculated
     * @param year
     * @param data
     */
    protected void onArchivePerformanceLoaded(int year, HashMap<Integer, double[]> data)
    {
        GWT.log("ArchivePerf is loaded");
        m_loadedFlag.setIsArchivePerformanceLoaded(true);
        ArchivePerf.getInstance().setData(data);
        ArchivePerf.getInstance().recalculateData();
        
        onCommonLoad();
    }

    /**
     * Handles successful load of DV ratio data from ITTC server
     * @param year
     * @param arRisks
     */
    protected void onDvRatioLoaded(int year, HashMap<Integer, double[]> arRisks)
    {
        m_loadedFlag.setIsDvRatioLoaded(true);
        GWT.log("DV ratio is loaded");
        DvRatio.getInstance().setData(arRisks);
        
        Por3.getInstance().sharpeRatio.setData(arRisks);

        onCommonLoad();
    }

    /**
     * Handles successfully load of Euro conversion from ITTC
     * @param year which year is loaded
     * @param ds array containing Euro conversion
     */
    protected void onEuroConversionLoaded(int year, double[][] ds)
    {
        GWT.log("Euro conversion is loaded");
        m_loadedFlag.setIsEuroConversionLoaded(true);
        EuroConversion.getInstance().setData(ds);
        EuroConversion.getInstance().recalculate();
        
        onCommonLoad();
    }
    
    protected void onFuturesContractsAmountLoaded(int year, HashMap<String, double[]> arValues)
    {
        // Fill Futures markets names list
        extractMarketsNamesForFutures(arValues);
        
        LoadedDataView.getInstance().setIsFuturesContractsAmountLoaded(true);
        FuturesContractsAmountView.getInstance().setData(arValues);
        FuturesContractsAmountView.getInstance().recalculate();
        FuturesContractsAmountView.getInstance().redrawFull();
    }

    protected void onFuturesTradesAmountLoaded(int year, HashMap<String, double[]> arValues)
    {
        LoadedDataView.getInstance().setIsFuturesTradesAmountLoaded(true);
        FuturesTradesAmountView.getInstance().setData(arValues);
        FuturesTradesAmountView.getInstance().recalculate();
        FuturesTradesAmountView.getInstance().redrawFull();
    }

    protected void onCommonLoad()
    {
        if( m_loadedFlag.m_isArchivePerformanceLoaded )
        {
            if( m_loadedFlag.m_isProfitLossesFxLoaded )
            {
                GWT.log("ProfitLossesPerformance is calculated (Archive and P/L are loaded)...");
                ProfitLossesPerformance.getInstance().recalculate();
            }

            if( m_loadedFlag.m_isDvRatioLoaded )
            {
                GWT.log("Archive performance is already loaded, let's calculate DV ratio...");
                DvRatio.getInstance().recalculate();
                Por3.getInstance().sharpeRatio.recalculate();
            }

        }
        
        // Sets markets names to display IndicesContractsTrades
        if( m_loadedFlag.m_isFuturesContractsAmountLoaded && m_loadedFlag.m_isFuturesTradesAmountLoaded )
        {
            ProfitLossesPor3View.getInstance().IndicesContractsTrades.setMarketsNames(getMarketsNamesForFutures());
            // Need to redraw headers
            ProfitLossesPor3View.getInstance().IndicesContractsTrades.redrawFull();
        }
    }

    protected String[] m_futuresMarketsNames = null;
    public String[] getMarketsNamesForFutures()
    {
        return m_futuresMarketsNames;
    }
    
    protected void extractMarketsNamesForFutures(HashMap<String, double[]> arValues)
    {
        int pos = 0;
        m_futuresMarketsNames = new String[arValues.keySet().size()];
        
        for( String name : arValues.keySet() )
        {
            m_futuresMarketsNames[pos++] = name;
        }
    }

    private void loadFuturesContractsAmount(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getfuturescontractsamount/?callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got JSON data for Contracts amount (Futures)...");
                if( json != null )
                {
                    HashMap<String, double[]> arValues = FuturesContractsAmountView.parseJsonData(json);
                    onFuturesContractsAmountLoaded(year, arValues);
                }
            }
        });
    }

    private void loadFuturesTradesAmount(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getfuturestradesamount/?callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got JSON data for Trades amount (Futures)...");
                if( json != null )
                {
                    HashMap<String, double[]> arValues = FuturesTradesAmountView.parseJsonData(json);
                    onFuturesTradesAmountLoaded(year, arValues);
                }
            }
        });
        
    }

    protected void loadDvRatio(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getdvratio/?callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got JSON data for DvRatio...");
                if( json != null )
                {
                    HashMap<Integer, double[]> arRisks = DvRatio.parseJsonData(json);
                    onDvRatioLoaded(year, arRisks);
                }
            }
        });
    }
    
    /**
     * Loads archive performance from ITTC server, parses and store it
     * @param year
     */
    protected void loadArchivePerformance(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getarchiveperf/?callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got archive performance from ITTC server");
                HashMap<Integer, double[]> arPerf = ArchivePerf.parseJsonData(json);
                onArchivePerformanceLoaded(year, arPerf);
            }
            
        });
    }

    /**
     * Loads Euro conversion data from ITTC server, parses and stores it
     * @param year
     */
    protected void loadEuroConversion(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/geteuroconv/?year=" + year + "&callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got EUR conversion from ITTC server");
                double[][] ds = EuroConversion.parseJsonData(json);
                onEuroConversionLoaded(year, ds);
            }
        });
    }

    protected void loadProfitLossesFx(final int year/*,  successHandler*/)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getprofitlosses/?year=" + year + "&callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Got profit/losses (Forex) from ITTC server");
                HashMap<String, double[]> data = parseProfitLossesJsonData(json);
//                JrcEventsLog.getInstance().add("Profit/losses are parsed OK");
                onProfitLossesFxLoaded(year, data);
            }
        });

    }
    
    protected void loadProfitLossesFutures(final int year)
    {
        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getprofitlosses/?asset=Futures&year=" + year + "&callback=", new JsonRequestHandler()
        {
            
            @Override
            public void onRequestCompleted(JavaScriptObject json)
            {
                JrcEventsLog.getInstance().add("Get profit/losses (Futures) from ITTC server");
                HashMap<String, double[]> data = parseProfitLossesJsonData(json);
                onProfitLossesFuturesLoaded(year, data);
            }
        });
    }

    protected HashMap<String, double[]> parseProfitLossesJsonData(JavaScriptObject json)
    {
        HashMap<String, double[]> data = new HashMap<String, double[]>();
        
        JSONObject jsonObj = new JSONObject(json);
        for( String k : jsonObj.keySet() )
        {
            JSONArray ar = jsonObj.get(k).isArray();
            double[] arValues = new double[12];
            for( int i = 0; i < ar.size(); i++ )
            {
                arValues[i] = Double.parseDouble(ar.get(i).toString());
            }
            data.put(k, arValues);
        }
        
        return data;
    }

    public ProfitLossesHolder getProfitLosses()
    {
        return m_profitLossesHolder;
    }
    
}
